package com.skt.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.skt.domain.DTO.LoginDTO;
import com.skt.domain.DTO.TokenPairDTO;
import com.skt.domain.ENUM.AccountStatus;
import com.skt.domain.PO.User;
import com.skt.domain.VO.LogoutResultVO;
import com.skt.domain.VO.UserVO;
import com.skt.exception.*;
import com.skt.mapper.UserMapper;
import com.skt.security.LoginUser;
import com.skt.service.AuthService;
import com.skt.service.BloomFilterService;
import com.skt.utils.JwtUtil;
import com.skt.utils.RedisUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 认证服务实现类
 * 负责用户注册、登录、登出、令牌刷新等认证相关业务逻辑
 *
 * @author YourName
 * @version 1.0
 * @since 2024
 */
@RequiredArgsConstructor
public class test extends ServiceImpl<UserMapper, User> implements AuthService {

    // ========== 日志记录器 ==========
    private static final Logger logger = LoggerFactory.getLogger(AuthServiceImpl.class);

    // ========== 依赖注入 ==========
    private final JwtUtil jwtUtil;                    // JWT工具类，用于令牌生成和验证
    private final RedisUtil redisUtil;                // Redis工具类，用于缓存操作
    private final UserMapper userMapper;              // 用户数据访问层
    private final PasswordEncoder passwordEncoder;    // 密码编码器，用于密码加密和验证
    private final BloomFilterService bloomFilterService; // 布隆过滤器服务，用于快速判断数据是否存在
    private final RedissonClient redissonClient;      // 分布式锁客户端，用于并发控制

    // ========== 业务常量定义 ==========
    /** 每个IP最大登录尝试次数，防止暴力破解 */
    private static final int MAX_LOGIN_ATTEMPTS_PER_IP = 10;
    /** 每个用户最大登录失败次数，超过则锁定账户 */
    private static final int MAX_LOGIN_FAILURES_PER_USER = 5;
    /** 账户锁定持续时间（分钟） */
    private static final int LOCK_DURATION_MINUTES = 30;
    /** 缓存过期时间（分钟） */
    private static final int CACHE_EXPIRE_MINUTES = 30;
    /** 缓存过期时间（小时） */
    private static final int CACHE_EXPIRE_HOURS = 24;

    // ========== 本地缓存实现 ==========
    /**
     * 本地缓存容器，使用ConcurrentHashMap保证线程安全
     * Key: 缓存键，格式如 "user_cache:123"
     * Value: 缓存条目，包含值和过期时间
     */
    private final Map<String, CacheEntry> localCache = new ConcurrentHashMap<>();

    /** 本地缓存过期时间：5分钟（毫秒） */
    private static final long LOCAL_CACHE_TTL = 5 * 60 * 1000;

    /**
     * 本地缓存条目类
     * 封装缓存值和过期时间，支持自动过期清理
     */
    private static class CacheEntry {
        Object value;       // 缓存的实际值
        long expireTime;    // 过期时间戳（毫秒）

        /**
         * 构造函数
         * @param value 缓存值
         * @param ttl 存活时间（毫秒）
         */
        CacheEntry(Object value, long ttl) {
            this.value = value;
            this.expireTime = System.currentTimeMillis() + ttl;
        }

        /**
         * 检查缓存是否已过期
         * @return true-已过期，false-未过期
         */
        boolean isExpired() {
            return System.currentTimeMillis() > expireTime;
        }
    }

    // ========== 接口方法实现 ==========

    /**
     * 用户注册接口
     * 处理新用户注册流程，包括参数验证、密码加密、数据持久化等
     *
     * @param loginDTO 注册请求参数，包含用户名、密码等信息
     * @return UserVO 注册成功的用户视图对象
     */
    @Override
    public UserVO register(LoginDTO loginDTO) {
        // TODO: 实现用户注册逻辑
        // 1. 参数验证（用户名、密码格式等）
        // 2. 检查用户名是否已存在
        // 3. 密码加密处理
        // 4. 用户信息持久化
        // 5. 生成初始令牌
        // 6. 返回用户视图对象
        return null;
    }

    /**
     * 用户登录接口
     * 处理用户登录认证，验证用户名密码，生成访问令牌
     *
     * @param loginDTO 登录请求参数，包含用户名、密码等信息
     * @return UserVO 登录成功的用户视图对象（包含令牌）
     */
    @Override
    public UserVO login(LoginDTO loginDTO) {
        // TODO: 实现用户登录逻辑
        // 1. 参数验证
        // 2. 查询用户信息
        // 3. 密码验证
        // 4. 生成访问令牌和刷新令牌
        // 5. 缓存用户会话信息
        // 6. 记录登录日志
        // 7. 返回用户视图对象
        return null;
    }

    /**
     * 令牌刷新接口
     * 使用刷新令牌获取新的访问令牌，延长会话有效期
     * 包含完整的令牌验证、安全检查和缓存更新流程
     *
     * @param refreshToken 刷新令牌字符串
     * @return UserVO 包含新令牌的用户视图对象
     * @throws AuthException 令牌无效、过期或验证失败时抛出
     */
    @Override
    public UserVO refreshToken(String refreshToken) {
        logger.info("开始刷新令牌: refreshToken={}",
                refreshToken != null ? refreshToken.substring(0, 10) + "..." : "null");

        // 1. 刷新令牌基础验证（格式、签名、过期时间等）
        Claims claims = validateRefreshToken(refreshToken);

        // 2. 从令牌声明中提取用户名，并查询对应用户信息
        String username = extractUsernameFromClaims(claims);
        User user = getUserByUsername(username);

        // 3. 验证用户状态（是否启用、是否锁定等）
        validateUserStatus(user);

        // 4. 分布式锁防并发刷新（同一用户同时刷新令牌）
        String refreshLockKey = "refresh_lock:" + username;
        RLock lock = redissonClient.getLock(refreshLockKey);
        boolean lockAcquired = false;

        try {
            // 尝试获取分布式锁，等待3秒，锁持有30秒
            lockAcquired = lock.tryLock(3, 30, TimeUnit.SECONDS);
            if (!lockAcquired) {
                logger.warn("刷新令牌操作频繁: username={}", username);
                throw AuthException.of(AuthErrorCode.REFRESH_OPERATION_FREQUENT);
            }

            // 5. 验证刷新令牌是否在有效会话中
            validateUserSession(user.getId(), refreshToken);

            // 6. 设备一致性检查（防止令牌被盗用）
            validateDeviceConsistency(user.getUsername());

            // 7. 使旧令牌失效（加入黑名单）
            invalidateOldTokens(user.getId(), refreshToken);

            // 8. 生成新的令牌对（访问令牌 + 刷新令牌）
            TokenPairDTO newTokens = generateNewTokenPair(user);

            // 9. 同步更新缓存中的令牌状态
            syncCacheAfterTokenRefresh(user, newTokens, refreshToken);

            // 10. 记录令牌刷新审计日志
            recordTokenRefreshAudit(user, newTokens);

            // 11. 构建并返回响应结果
            return buildRefreshTokenResponse(user, newTokens);

        } catch (InterruptedException e) {
            // 线程中断处理
            Thread.currentThread().interrupt();
            logger.error("令牌刷新操作被中断: username={}", username, e);
            throw AuthException.of(AuthErrorCode.SYSTEM_ERROR);
        } finally {
            // 确保释放分布式锁
            if (lockAcquired) {
                lock.unlock();
                logger.debug("释放刷新令牌分布式锁: username={}", username);
            }
        }
    }

    /**
     * 用户登出接口
     * 处理用户主动登出操作，支持单设备登出和全设备登出
     * 包含令牌失效、缓存清理、安全上下文清除等操作
     *
     * @param logoutAllDevices 是否登出所有设备 true-全设备登出 false-当前设备登出
     * @return LogoutResultVO 登出结果视图对象
     * @throws AuthException 认证失败或操作频繁时抛出
     */
    @Override
    public LogoutResultVO logout(Boolean logoutAllDevices) {
        logger.info("用户登出开始: logoutAllDevices={}", logoutAllDevices);

        // 1. 从安全上下文中获取认证信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            logger.error("安全上下文中无认证信息");
            throw AuthException.of(AuthErrorCode.TOKEN_MISSING);
        }

        // 2. 从认证信息中提取登录用户信息
        LoginUser loginUser = exactLoginUser(authentication);
        Long userId = loginUser.getUserId();

        // 3. 从请求头中获取当前访问令牌
        String token = getCurrentToken();

        // 4. 使用分布式锁防止并发登出操作
        String lockKey = "logout_lock:" + userId;
        RLock lock = redissonClient.getLock(lockKey);
        boolean lockAcquired = false;

        try {
            // 尝试获取锁，等待3秒，锁持有30秒
            lockAcquired = lock.tryLock(3, 30, TimeUnit.SECONDS);
            if (!lockAcquired) {
                logger.warn("登出操作频繁: userId={}", userId);
                throw AuthException.of(AuthErrorCode.LOGOUT_OPERATION_FREQUENT);
            }

            // 5. 查询用户详细信息
            User user = getUserById(userId);

            // 6. 获取客户端IP地址（用于审计日志）
            String clientIP = getClientIP();

            // 7. 执行核心登出逻辑
            LogoutResultVO result = executeLogout(user, token, logoutAllDevices, clientIP);

            logger.info("用户登出成功: userId={}, logoutAllDevices={}", userId, logoutAllDevices);
            return result;

        } catch (InterruptedException e) {
            // 线程中断处理
            Thread.currentThread().interrupt();
            logger.error("登出操作被中断: userId={}", userId, e);
            throw AuthException.of(AuthErrorCode.SYSTEM_ERROR);
        } finally {
            // 确保释放分布式锁
            if (lockAcquired) {
                lock.unlock();
                logger.debug("释放登出分布式锁: userId={}", userId);
            }
        }
    }



    // ========== 私有方法实现 ==========

    /**
     * 执行登出核心逻辑
     * 包含令牌失效、缓存清理、审计日志记录等操作
     *
     * @param user 用户实体对象
     * @param token 当前访问令牌
     * @param logoutAllDevices 是否登出所有设备
     * @param clientIP 客户端IP地址
     * @return LogoutResultVO 登出结果
     */
    private LogoutResultVO executeLogout(User user, String token, Boolean logoutAllDevices, String clientIP) {
        Long userId = user.getId();
        String username = user.getUsername();

        try {
            // 1. 令牌失效处理：将令牌加入黑名单，防止继续使用
            invalidateTokens(userId, token, logoutAllDevices);

            // 2. 清理缓存数据：清除用户会话相关的缓存信息
            clearUserCache(userId, username, logoutAllDevices);

            // 3. 清理安全上下文：清除Spring Security的认证信息
            clearSecurityContext();

            // 4. 记录审计日志：记录登出操作详情
            recordLogoutAudit(user, logoutAllDevices, clientIP);

            // 5. 构建返回结果
            return buildLogoutResultVO(user, logoutAllDevices, clientIP);

        } catch (Exception e) {
            logger.error("登出执行异常: userId={}, error={}", userId, e.getMessage());
            throw AuthException.of(AuthErrorCode.LOGOUT_FAILED);
        }
    }

    /**
     * 构建登出结果视图对象
     * 根据登出操作结果生成详细的响应信息
     *
     * @param user 用户实体对象
     * @param logoutAllDevices 是否登出所有设备
     * @param clientIP 客户端IP地址
     * @return LogoutResultVO 登出结果视图对象
     */
    private LogoutResultVO buildLogoutResultVO(User user, Boolean logoutAllDevices, String clientIP) {
        LogoutResultVO result = new LogoutResultVO();

        // 设置用户基本信息
        result.setUserId(user.getId());
        result.setUsername(user.getUsername());
        result.setRealName(user.getRealName());

        // 设置角色和状态信息
        result.setRole(user.getRole());
        result.setAccountStatus(user.getAccountStatus());
        result.setRoleStatus(user.getRoleStatus());

        // 设置登出操作信息
        result.setLogoutTime(LocalDateTime.now());
        result.setLogoutType("ACTIVE"); // 主动登出
        result.setLogoutAllDevices(logoutAllDevices);
        result.setClientIp(clientIP);

        // 设置店铺信息（如果用户有关联店铺）
        if (user.getShopName() != null) {
            result.setShopName(user.getShopName());
        }

        // 计算影响的会话数量
        result.setAffectedSessionCount(logoutAllDevices ? getActiveSessionCount(user.getId()) : 1);
        result.setMessage("登出成功");

        return result;
    }

    /**
     * 验证刷新令牌的有效性
     * 检查令牌格式、签名、过期时间、黑名单状态等
     *
     * @param refreshToken 刷新令牌字符串
     * @return Claims 令牌声明信息
     * @throws AuthException 令牌无效时抛出
     */
    private Claims validateRefreshToken(String refreshToken) {
        // 基础空值检查
        if (StringUtils.isBlank(refreshToken)) {
            throw AuthException.of(AuthErrorCode.REFRESH_TOKEN_MISSING);
        }

        try {
            // 1. 解析令牌声明
            Claims claims = jwtUtil.parseClaims(refreshToken);

            // 2. 检查令牌是否过期
            if (claims.getExpiration().before(new Date())) {
                throw AuthException.of(AuthErrorCode.REFRESH_TOKEN_EXPIRED);
            }

            // 3. 检查令牌是否被加入黑名单
            String blacklistKey = "refresh_token_blacklist:" + refreshToken;
            if (Boolean.TRUE.equals(redisUtil.hasKey(blacklistKey))) {
                throw AuthException.of(AuthErrorCode.REFRESH_TOKEN_REVOKED);
            }

            return claims;

        } catch (ExpiredJwtException e) {
            // 令牌过期异常
            logger.warn("刷新令牌已过期: {}", e.getMessage());
            throw AuthException.of(AuthErrorCode.REFRESH_TOKEN_EXPIRED);
        } catch (JwtException e) {
            // JWT解析异常（签名错误、格式错误等）
            logger.warn("刷新令牌解析失败: {}", e.getMessage());
            throw AuthException.of(AuthErrorCode.REFRESH_TOKEN_INVALID);
        } catch (Exception e) {
            // 其他系统异常
            logger.error("验证刷新令牌异常: {}", e.getMessage());
            throw AuthException.of(AuthErrorCode.SYSTEM_ERROR);
        }
    }

    /**
     * 从令牌声明中提取用户名
     *
     * @param claims JWT声明对象
     * @return 用户名字符串
     * @throws AuthException 用户名为空时抛出
     */
    private String extractUsernameFromClaims(Claims claims) {
        try {
            String username = claims.getSubject();
            if (StringUtils.isBlank(username)) {
                logger.error("刷新令牌中用户名为空");
                throw AuthException.of(AuthErrorCode.REFRESH_TOKEN_INVALID);
            }
            return username;
        } catch (Exception e) {
            logger.error("从令牌提取用户名失败", e);
            throw AuthException.of(AuthErrorCode.REFRESH_TOKEN_INVALID);
        }
    }

    /**
     * 根据用户名查询用户信息
     *
     * @param username 用户名
     * @return 用户实体对象
     * @throws AuthException 用户不存在或查询失败时抛出
     */
    private User getUserByUsername(String username) {
        try {
            User user = userMapper.selectOne(new QueryWrapper<User>().eq("username", username));
            if (user == null) {
                logger.warn("用户不存在: username={}", username);
                throw AuthException.of(AuthErrorCode.USER_NOT_EXIST);
            }
            return user;
        } catch (Exception e) {
            logger.error("查询用户信息失败: username={}", username, e);
            throw AuthException.of(AuthErrorCode.SYSTEM_ERROR);
        }
    }

    /**
     * 验证用户账户状态
     * 检查用户是否被禁用、锁定等异常状态
     *
     * @param user 用户实体对象
     * @throws AuthException 账户状态异常时抛出
     */
    private void validateUserStatus(User user) {
        if (user.getAccountStatus() != AccountStatus.ENABLED) {
            logger.warn("用户账户状态异常: username={}, status={}",
                    user.getUsername(), user.getAccountStatus());
            throw AuthException.of(AuthErrorCode.ACCOUNT_DISABLED);
        }
    }

    /**
     * 验证用户会话有效性
     * 检查刷新令牌是否在用户的活跃会话中
     *
     * @param userId 用户ID
     * @param refreshToken 刷新令牌
     * @throws AuthException 令牌不在会话中时抛出
     */
    private void validateUserSession(Long userId, String refreshToken) {
        try {
            String userTokensKey = "user_tokens:" + userId;
            Boolean tokenExists = redisUtil.hHasKey(userTokensKey, refreshToken);

            if (Boolean.FALSE.equals(tokenExists)) {
                logger.warn("刷新令牌不在有效会话中: userId={}", userId);
                throw AuthException.of(AuthErrorCode.REFRESH_TOKEN_NOT_IN_SESSION);
            }

            logger.debug("用户会话验证通过: userId={}", userId);
        } catch (AuthException e) {
            // 直接抛出业务异常
            throw e;
        } catch (Exception e) {
            logger.error("验证用户会话失败: userId={}, error={}", userId, e.getMessage());
            throw AuthException.of(AuthErrorCode.SYSTEM_ERROR);
        }
    }

    /**
     * 验证设备一致性
     * 检查当前设备与上次登录设备是否一致，防止令牌盗用
     *
     * @param username 用户名
     */
    private void validateDeviceConsistency(String username) {
        try {
            String deviceKey = "login_device:" + username;
            String currentDevice = getCurrentDevice();
            String lastDevice = (String) redisUtil.get(deviceKey);

            if (lastDevice != null && !lastDevice.equals(currentDevice)) {
                logger.warn("设备变更检测: username={}, lastDevice={}, currentDevice={}",
                        username, lastDevice, currentDevice);
                // 可以根据安全策略决定是否抛出异常或只是记录警告
            }
        } catch (Exception e) {
            logger.warn("设备一致性检查失败: username={}, error={}", username, e.getMessage());
        }
    }

    /**
     * 使旧令牌失效
     * 将使用过的刷新令牌加入黑名单，防止重复使用
     *
     * @param userId 用户ID
     * @param refreshToken 刷新令牌
     */
    private void invalidateOldTokens(Long userId, String refreshToken) {
        try {
            // 1. 计算令牌剩余有效时间
            long ttl = getTokenRemainingTime(refreshToken);
            if (ttl > 0) {
                // 2. 将令牌加入黑名单（在剩余时间内有效）
                String blacklistKey = "refresh_token_blacklist:" + refreshToken;
                redisUtil.set(blacklistKey, "1", ttl, TimeUnit.MILLISECONDS);
            }

            // 3. 清理令牌映射关系
            redisUtil.delete("token_user:" + refreshToken);
            redisUtil.hDelete("user_tokens:" + userId, refreshToken);

            logger.debug("旧令牌失效处理完成: userId={}", userId);
        } catch (Exception e) {
            logger.warn("失效旧令牌失败: userId={}, error={}", userId, e.getMessage());
        }
    }

    /**
     * 生成新的令牌对
     * 创建新的访问令牌和刷新令牌
     *
     * @param user 用户实体对象
     * @return TokenPairDTO 令牌对数据传输对象
     * @throws AuthException 令牌生成失败时抛出
     */
    private TokenPairDTO generateNewTokenPair(User user) {
        try {
            // 1. 生成新的访问令牌（短期令牌，通常30分钟）
            String newAccessToken = jwtUtil.generateAccessToken(user.getUsername());

            // 2. 生成新的刷新令牌（长期令牌，通常7天）
            String newRefreshToken = jwtUtil.generateRefreshToken(user.getUsername());

            // 3. 解析令牌获取过期时间信息
            Claims accessTokenClaims = jwtUtil.parseClaims(newAccessToken);
            Claims refreshTokenClaims = jwtUtil.parseClaims(newRefreshToken);

            Long accessTokenExpireTime = accessTokenClaims.getExpiration().getTime();
            Long refreshTokenExpireTime = refreshTokenClaims.getExpiration().getTime();

            // 4. 构建令牌对DTO
            TokenPairDTO tokenPair = new TokenPairDTO();
            tokenPair.setAccessToken(newAccessToken);
            tokenPair.setRefreshToken(newRefreshToken);
            tokenPair.setAccessTokenExpiresIn(accessTokenExpireTime);
            tokenPair.setRefreshTokenExpiresIn(refreshTokenExpireTime);
            tokenPair.setExpireTime(accessTokenExpireTime); // 向后兼容

            logger.debug("生成新令牌对成功: userId={}, accessExpires={}, refreshExpires={}",
                    user.getId(),
                    new Date(accessTokenExpireTime),
                    new Date(refreshTokenExpireTime));

            return tokenPair;
        } catch (Exception e) {
            logger.error("生成令牌对失败: userId={}, error={}", user.getId(), e.getMessage());
            throw AuthException.of(AuthErrorCode.TOKEN_GENERATE_FAILED);
        }
    }

    /**
     * 同步缓存状态
     * 在令牌刷新后更新缓存中的令牌信息
     *
     * @param user 用户实体对象
     * @param newTokens 新的令牌对
     * @param oldRefreshToken 旧的刷新令牌
     */
    private void syncCacheAfterTokenRefresh(User user, TokenPairDTO newTokens, String oldRefreshToken) {
        try {
            Long userId = user.getId();
            String newRefreshToken = newTokens.getRefreshToken();
            String username = user.getUsername();

            // 1. 删除旧令牌的映射关系
            redisUtil.hDelete("user_tokens:" + userId, oldRefreshToken);
            redisUtil.delete("token_user:" + oldRefreshToken);

            // 2. 准备新令牌的缓存信息
            Map<String, Object> tokenInfo = new HashMap<>();
            tokenInfo.put("accessToken", newTokens.getAccessToken());
            tokenInfo.put("createTime", System.currentTimeMillis());
            tokenInfo.put("device", getCurrentDevice());
            tokenInfo.put("userAgent", getUserAgent());

            // 3. 计算令牌的剩余有效时间
            long accessTokenExpireTime = newTokens.getAccessTokenExpiresIn();
            long refreshTokenExpireTime = newTokens.getRefreshTokenExpiresIn();

            long accessTokenTTL = Math.max(0, accessTokenExpireTime - System.currentTimeMillis());
            long refreshTokenTTL = Math.max(0, refreshTokenExpireTime - System.currentTimeMillis());

            // 4. 设置用户令牌映射（与刷新令牌同生命周期）
            redisUtil.hPut("user_tokens:" + userId, newRefreshToken, tokenInfo);
            redisUtil.expire("user_tokens:" + userId, refreshTokenTTL, TimeUnit.MILLISECONDS);

            // 5. 设置令牌-用户映射（快速查找令牌对应的用户）
            redisUtil.set("token_user:" + newRefreshToken, userId, refreshTokenTTL, TimeUnit.MILLISECONDS);

            // 6. 设置用户登录状态
            redisUtil.set("user_login_status:" + userId, "online", refreshTokenTTL, TimeUnit.MILLISECONDS);

            // 7. 设置设备信息
            String deviceKey = "login_device:" + username;
            redisUtil.set(deviceKey, getCurrentDevice(), refreshTokenTTL, TimeUnit.MILLISECONDS);

            logger.debug("缓存同步完成: userId={}, accessTokenTTL={}分钟, refreshTokenTTL={}天",
                    userId,
                    accessTokenTTL / (60 * 1000),
                    refreshTokenTTL / (24 * 60 * 60 * 1000));

        } catch (Exception e) {
            logger.error("同步缓存状态失败: userId={}, error={}", user.getId(), e.getMessage());
        }
    }

    /**
     * 记录令牌刷新审计日志
     *
     * @param user 用户实体对象
     * @param newTokens 新的令牌对
     */
    private void recordTokenRefreshAudit(User user, TokenPairDTO newTokens) {
        try {
            Map<String, Object> auditLog = new HashMap<>();
            auditLog.put("userId", user.getId());
            auditLog.put("username", user.getUsername());
            auditLog.put("refreshTime", System.currentTimeMillis());
            auditLog.put("newAccessTokenPrefix", newTokens.getAccessToken().substring(0, 10) + "...");
            auditLog.put("newRefreshTokenPrefix", newTokens.getRefreshToken().substring(0, 10) + "...");
            auditLog.put("expireTime", newTokens.getExpireTime());
            auditLog.put("userAgent", getUserAgent());
            auditLog.put("clientIP", getClientIP());

            String auditKey = "refresh_token_audit:" + user.getId() + ":" + System.currentTimeMillis();
            redisUtil.set(auditKey, auditLog, 7, TimeUnit.DAYS);

            logger.info("刷新令牌审计日志记录: userId={}, username={}", user.getId(), user.getUsername());
        } catch (Exception e) {
            logger.warn("记录刷新令牌审计日志失败: {}", e.getMessage());
        }
    }

    /**
     * 构建刷新令牌响应
     *
     * @param user 用户实体对象
     * @param newTokens 新的令牌对
     * @return UserVO 用户视图对象
     */
    private UserVO buildRefreshTokenResponse(User user, TokenPairDTO newTokens) {
        try {
            UserVO userVO = new UserVO();

            // 设置用户基本信息
            userVO.setId(user.getId());
            userVO.setUsername(user.getUsername());
            userVO.setRealName(user.getRealName());
            userVO.setPhone(user.getPhone());
            userVO.setEmail(user.getEmail());

            // 设置角色和状态信息
            if (user.getRole() != null) {
                userVO.setRole(user.getRole().getValue().byteValue());
            }
            if (user.getAccountStatus() != null) {
                userVO.setStatus(user.getAccountStatus().getValue().byteValue());
            }

            // 设置时间信息
            userVO.setCreatedAt(user.getCreatedAt());
            userVO.setUpdatedAt(user.getUpdatedAt());

            // 设置新的访问令牌
            userVO.setToken(newTokens.getAccessToken());

            logger.info("令牌刷新成功: userId={}, username={}", user.getId(), user.getUsername());
            return userVO;

        } catch (Exception e) {
            logger.error("构建响应结果失败: userId={}, error={}", user.getId(), e.getMessage());
            throw AuthException.of(AuthErrorCode.SYSTEM_ERROR);
        }
    }

    // ========== 辅助方法 ==========

    /**
     * 获取令牌剩余有效时间
     *
     * @param token JWT令牌
     * @return 剩余时间（毫秒）
     */
    private long getTokenRemainingTime(String token) {
        try {
            Claims claims = jwtUtil.parseClaims(token);

            Date expiration = claims.getExpiration();
            return Math.max(0, expiration.getTime() - System.currentTimeMillis());
        } catch (Exception e) {
            logger.warn("获取令牌剩余时间失败，使用默认值: {}", e.getMessage());
            return 3600000L; // 默认1小时
        }
    }

    /**
     * 失效令牌（单设备或全设备）
     *
     * @param userId 用户ID
     * @param currentToken 当前令牌
     * @param logoutAllDevices 是否登出所有设备
     */
    private void invalidateTokens(Long userId, String currentToken, Boolean logoutAllDevices) {
        if (Boolean.TRUE.equals(logoutAllDevices)) {
            // 全设备登出：失效所有令牌
            invalidateAllUserTokens(userId);
        } else {
            // 单设备登出：只失效当前令牌
            invalidateSingleToken(currentToken, userId);
        }
    }

    /**
     * 失效用户所有令牌（全设备登出）
     *
     * @param userId 用户ID
     */
    private void invalidateAllUserTokens(Long userId) {
        try {
            // 1. 获取用户所有活跃令牌
            String userTokensKey = "user_tokens:" + userId;
            Map<Object, Object> tokenMap = redisUtil.hEntries(userTokensKey);

            if (tokenMap != null && !tokenMap.isEmpty()) {
                // 2. 批量加入黑名单
                for (Object tokenObj : tokenMap.keySet()) {
                    String token = (String) tokenObj;
                    invalidateSingleToken(token, userId);
                }
            }

            // 3. 清理用户所有令牌记录
            redisUtil.delete(userTokensKey);
            redisUtil.delete("user_sessions:" + userId);

            logger.info("全设备令牌失效: userId={}, tokensCount={}", userId,
                    tokenMap != null ? tokenMap.size() : 0);
        } catch (Exception e) {
            logger.error("失效用户所有令牌失败: userId={}, error={}", userId, e.getMessage());
            throw e;
        }
    }

    /**
     * 失效单个令牌
     *
     * @param token 令牌字符串
     * @param userId 用户ID
     */
    private void invalidateSingleToken(String token, Long userId) {
        try {
            // 1. 计算令牌剩余有效期
            long ttl = getTokenRemainingTime(token);
            if (ttl > 0) {
                // 2. 将令牌加入黑名单
                String blacklistKey = "token_blacklist:" + token;
                redisUtil.set(blacklistKey, "1", ttl, TimeUnit.SECONDS);
            }

            // 3. 清理令牌映射关系
            redisUtil.delete("token_user:" + token);
            redisUtil.hDelete("user_tokens:" + userId, token);

            logger.debug("单设备令牌失效: userId={}, token={}", userId, token.substring(0, 10) + "...");
        } catch (Exception e) {
            logger.warn("失效单令牌失败: userId={}, error={}", userId, e.getMessage());
        }
    }

    /**
     * 清理用户缓存
     *
     * @param userId 用户ID
     * @param username 用户名
     * @param logoutAllDevices 是否登出所有设备
     */
    private void clearUserCache(Long userId, String username, Boolean logoutAllDevices) {
        try {
            if (Boolean.TRUE.equals(logoutAllDevices)) {
                // 全设备登出：清理所有会话相关缓存
                redisUtil.delete("user_login_status:" + userId);
                redisUtil.delete("user_devices:" + userId);
                redisUtil.delete("user_last_login:" + userId);
                redisUtil.delete("user_online_status:" + userId);
            } else {
                // 单设备登出：只清理当前设备会话
                String deviceFingerprint = getDeviceFingerprint();
                redisUtil.hDelete("user_devices:" + userId, deviceFingerprint);
                redisUtil.delete("user_login_status:" + userId);
            }

            logger.debug("用户缓存清理完成: userId={}, logoutAllDevices={}", userId, logoutAllDevices);
        } catch (Exception e) {
            logger.warn("清理用户缓存失败: userId={}, error={}", userId, e.getMessage());
        }
    }

    /**
     * 清理安全上下文
     */
    private void clearSecurityContext() {
        SecurityContextHolder.clearContext();
    }

    /**
     * 记录登出审计日志
     *
     * @param user 用户实体对象
     * @param logoutAllDevices 是否登出所有设备
     * @param clientIP 客户端IP
     */
    private void recordLogoutAudit(User user, Boolean logoutAllDevices, String clientIP) {
        try {
            Map<String, Object> auditLog = new HashMap<>();
            auditLog.put("userId", user.getId());
            auditLog.put("username", user.getUsername());
            auditLog.put("logoutTime", System.currentTimeMillis());
            auditLog.put("logoutType", "ACTIVE");
            auditLog.put("logoutAllDevices", logoutAllDevices);
            auditLog.put("clientIP", clientIP);
            auditLog.put("userAgent", getUserAgent());

            String auditKey = "logout_audit:" + user.getId() + ":" + System.currentTimeMillis();
            redisUtil.set(auditKey, auditLog, 7, TimeUnit.DAYS);

            logger.info("登出审计记录: userId={}, username={}, ip={}, logoutAllDevices={}",
                    user.getId(), user.getUsername(), clientIP, logoutAllDevices);
        } catch (Exception e) {
            logger.warn("记录登出审计日志失败: {}", e.getMessage());
        }
    }

    /**
     * 获取活跃会话数量
     *
     * @param userId 用户ID
     * @return 活跃会话数量
     */
    private Integer getActiveSessionCount(Long userId) {
        try {
            String userTokensKey = "user_tokens:" + userId;
            Map<Object, Object> tokenMap = redisUtil.hEntries(userTokensKey);
            return tokenMap != null ? tokenMap.size() : 0;
        } catch (Exception e) {
            logger.warn("获取活跃会话数量失败: userId={}", userId);
            return 0;
        }
    }

    /**
     * 获取当前设备指纹
     * 基于User-Agent等请求头生成设备唯一标识
     *
     * @return 设备指纹字符串
     */
    private String getCurrentDevice() {
        try {
            HttpServletRequest request = getCurrentRequest();
            String userAgent = request.getHeader("User-Agent");
            String accept = request.getHeader("Accept");
            String acceptLanguage = request.getHeader("Accept-Language");
            String acceptEncoding = request.getHeader("Accept-Encoding");

            String deviceFingerprint = userAgent + "|" + accept + "|" + acceptLanguage + "|" + acceptEncoding;
            return String.valueOf(deviceFingerprint.hashCode());
        } catch (Exception e) {
            logger.warn("获取设备指纹失败", e);
            return "unknown_device";
        }
    }

    /**
     * 获取设备指纹（简化版）
     *
     * @return 设备指纹字符串
     */
    private String getDeviceFingerprint() {
        try {
            HttpServletRequest request = getCurrentRequest();
            String userAgent = request.getHeader("User-Agent");
            String accept = request.getHeader("Accept");
            String acceptLanguage = request.getHeader("Accept-Language");
            String acceptEncoding = request.getHeader("Accept-Encoding");

            String fingerprint = userAgent + "|" + accept + "|" + acceptLanguage + "|" + acceptEncoding;
            return String.valueOf(fingerprint.hashCode());
        } catch (Exception e) {
            logger.warn("获取设备指纹失败", e);
            return "unknown_device";
        }
    }

    /**
     * 获取客户端真实IP地址
     * 处理代理服务器转发的情况
     *
     * @return 客户端IP地址
     */
    private String getClientIP() {
        try {
            HttpServletRequest request = getCurrentRequest();

            // 1. 检查X-Forwarded-For头（代理服务器转发）
            String ip = request.getHeader("X-Forwarded-For");
            if (StringUtils.isNotEmpty(ip) && !"unknown".equalsIgnoreCase(ip)) {
                return ip.split(",")[0].trim();
            }

            // 2. 检查X-Real-IP头（Nginx代理）
            ip = request.getHeader("X-Real-IP");
            if (StringUtils.isNotEmpty(ip) && !"unknown".equalsIgnoreCase(ip)) {
                return ip;
            }

            // 3. 使用远程地址（直接连接）
            return request.getRemoteAddr();
        } catch (Exception e) {
            logger.warn("获取客户端IP失败", e);
            return "unknown";
        }
    }

    /**
     * 获取User-Agent头信息
     *
     * @return User-Agent字符串
     */
    private String getUserAgent() {
        try {
            return getCurrentRequest().getHeader("User-Agent");
        } catch (Exception e) {
            return "unknown";
        }
    }

    /**
     * 获取当前HTTP请求对象
     *
     * @return HttpServletRequest对象
     */
    private HttpServletRequest getCurrentRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

    /**
     * 根据用户ID查询用户信息
     *
     * @param userId 用户ID
     * @return 用户实体对象
     * @throws AuthException 用户不存在时抛出
     */
    private User getUserById(Long userId) {
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw AuthException.of(AuthErrorCode.USER_NOT_EXIST);
        }
        return user;
    }

    /**
     * 从请求头中获取当前访问令牌
     *
     * @return 访问令牌字符串
     * @throws AuthException 令牌无效时抛出
     */
    private String getCurrentToken() {
        try {
            HttpServletRequest request = getCurrentRequest();
            String authHeader = request.getHeader("Authorization");
            if (authHeader != null && authHeader.startsWith("Bearer ")) {
                return authHeader.substring(7);
            }
            throw AuthException.of(AuthErrorCode.TOKEN_INVALID);
        } catch (Exception e) {
            throw AuthException.of(AuthErrorCode.TOKEN_INVALID);
        }
    }

    /**
     * 从认证信息中提取登录用户对象
     *
     * @param authentication Spring Security认证对象
     * @return 登录用户对象
     * @throws AuthException 认证信息无效时抛出
     */
    private LoginUser exactLoginUser(Authentication authentication) {
        Object principal = authentication.getPrincipal();
        if (principal instanceof LoginUser) {
            return (LoginUser) principal;
        }
        throw AuthException.of(AuthErrorCode.TOKEN_INVALID);
    }
}